package com.fa4j.common.data.repository.handler;

import com.fa4j.common.base.model.BaseEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.util.ObjectUtils;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@MappedTypes({List.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class ListBaseEnumTypeHandler<E extends BaseEnum> extends BaseTypeHandler<List<E>> {

    private final Class<E> enumClass;

    public ListBaseEnumTypeHandler(Class<E> enumClass) {
        if (enumClass == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.enumClass = enumClass;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<E> parameter, JdbcType jdbcType)
            throws SQLException {
        ps.setString(i, parameter.stream().map(e -> e.getCode().toString()).collect(Collectors.joining(",")));
    }

    @Override
    public List<E> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        final String str = rs.getString(columnName);
        return ObjectUtils.isEmpty(str) ? null
                : Arrays.stream(str.split(",")).map(this::getByCode).collect(Collectors.toList());
    }

    private E getByCode(String codeStr) {
        if (codeStr == null || codeStr.isEmpty())
            return null;
        if (enumClass.isEnum()) {
            Integer code = Integer.parseInt(codeStr);
            for (E e : enumClass.getEnumConstants()) {
                if (Objects.equals(code, e.getCode())) {
                    return e;
                }
            }
        }
        return null;
    }

    @Override
    public List<E> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        final String str = rs.getString(columnIndex);
        return ObjectUtils.isEmpty(str) ? null
                : Arrays.stream(str.split(",")).map(this::getByCode).collect(Collectors.toList());
    }

    @Override
    public List<E> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        final String str = cs.getString(columnIndex);
        return ObjectUtils.isEmpty(str) ? null
                : Arrays.stream(str.split(",")).map(this::getByCode).collect(Collectors.toList());
    }
}
